package com.lsh.ofc.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lsh.base.common.exception.BusinessException;
import com.lsh.base.common.model.CommonResult;
import com.lsh.ofc.core.constant.Constants;
import com.lsh.ofc.core.entity.*;
import com.lsh.ofc.core.enums.*;
import com.lsh.ofc.core.exception.EC;
import com.lsh.ofc.core.handler.AsyncTaskHandler;
import com.lsh.ofc.core.handler.CreateSoHandler;
import com.lsh.ofc.core.handler.logistics.AbstractLogisticsHandler;
import com.lsh.ofc.core.redis.RedisTemplate;
import com.lsh.ofc.core.service.*;
import com.lsh.ofc.core.util.OFCUtils;
import com.lsh.ofc.proxy.context.WumartBasicContext;
import com.lsh.ofc.proxy.service.AtpServiceProxy;
import com.lsh.ofc.proxy.util.MethodCallLogCollector;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * @author huangdong
 * @date 16/9/10
 */
@Service
public class OfcSoCreateServiceImpl implements OfcSoCreateService {

    private final Logger logger = Logger.getLogger(this.getClass());

    private ExecutorService executor = Executors.newFixedThreadPool(20);

    @Autowired
    private OfcOrderService ofcOrderService;

    @Autowired
    private OfcSoService ofcSoService;

    @Autowired
    private OfcSoSplitService ofcSoSplitService;

    @Autowired
    private OfcCustomerService ofcCustomerService;

    @Autowired
    private ApplicationContext context;

    @Autowired
    private AsyncTaskHandler asyncTaskHandler;

    @Autowired
    private RedisTemplate redisTemplate;

    @Transactional
    @Override
    public List<OfcSoHead> prepare(OfcOrderHead order) throws BusinessException {
        if (order == null) {
            throw EC.ERROR.exception("入参订单信息为空！");
        }
        List<OfcOrderDetail> details = order.getDetails();
        if (CollectionUtils.isEmpty(details)) {
            throw EC.ERROR.exception("入参订单明细为空！");
        }

        Long orderCode = order.getOrderCode();
        OfcSoHead filter = new OfcSoHead();
        filter.setOrderCode(orderCode);
        List<OfcSoHead> sos = this.ofcSoService.findList(filter, true);
        if (!CollectionUtils.isEmpty(sos)) {
            return sos;
        }
        //订单拆分
        sos = this.ofcSoSplitService.split(order);
        //TODO:目前不涉及多物理仓库履约拆单，三仓模式，此处不能取 sos.get(0).getWarehouseCode(),此处按照/分割开存入吧。
        //更新订单仓库信息和明细扩展信息
        Set<String> warehouseCodes = new HashSet<>();
        Set<String> warehouseNames = new HashSet<>();
        for (OfcSoHead so : sos) {
            warehouseCodes.add(so.getWarehouseCode());
            warehouseNames.add(so.getWarehouseName());
        }
        String warehouseCode = StringUtils.collectionToDelimitedString(warehouseCodes, "|");
        String warehouseName = StringUtils.collectionToDelimitedString(warehouseNames, "|");

        int ret = this.updateOrder4Prepare(order, warehouseCode, warehouseName);
        //TODO:防重入
        if (ret != 1) {
            throw EC.ERROR.exception("订单不存在，或者订单已拆单！订单号=" + orderCode);
        }

        //插入SO信息
        this.ofcSoService.insert(sos);
        return sos;
    }

    /**
     * 更新OFC订单信息
     *
     * @param order         订单
     * @param warehouseCode warehouseCode
     * @param warehouseName warehouseName
     * @return 返回值
     * @throws BusinessException 异常
     */
    private int updateOrder4Prepare(OfcOrderHead order, String warehouseCode, String warehouseName) throws BusinessException {
        OfcOrderHead filter = new OfcOrderHead();
        filter.setId(order.getId());
        filter.setOrderCode(order.getOrderCode());
        filter.setWarehouseCode("");
        filter.setWarehouseName("");

        OfcOrderHead updateHead = new OfcOrderHead();
        updateHead.setWarehouseCode(warehouseCode);
        updateHead.setWarehouseName(warehouseName);

        List<OfcOrderDetail> details = order.getDetails();
        if (!CollectionUtils.isEmpty(details)) {
            List<OfcOrderDetail> updateDetails = new ArrayList<>(details.size());
            for (OfcOrderDetail detail : order.getDetails()) {
                OfcOrderDetail updateDetail = new OfcOrderDetail();
                updateDetail.setId(detail.getId());
                updateDetail.setExt(detail.getExt());
                updateDetails.add(updateDetail);
            }
            updateHead.setDetails(updateDetails);
        }
        return this.ofcOrderService.update(updateHead, filter);
    }


    @Override
    public int process(OfcOrderHead ofcOrderHead, List<OfcSoHead> sos, Integer channel) throws BusinessException {
        if (CollectionUtils.isEmpty(sos)) {
            return -1;
        }
        Long orderCode = sos.get(0).getOrderCode();
        logger.info("process start...订单号= " + orderCode);
        List<Future<Boolean>> futures = new ArrayList<>(sos.size());
        for (OfcSoHead so : sos) {
            if (so == null || CollectionUtils.isEmpty(so.getDetails())) {
                continue;
            }
            if (channel == null) {
                if (!SoStatus.UNCREATED.getValue().equals(so.getSoStatus())) {
                    logger.warn("【" + so.getSoBillCode() + "】SO单状态(" + so.getSoStatus() + ")不为\"" + SoStatus.UNCREATED.getValue() + "\"");
                    continue;
                }
            }
            if (null != channel) {
                so.setFulfillChannel(channel);
                so.setPushPsiFlag("1");
            } else {
                so.setPushPsiFlag("0");
            }

            String soExt = so.getExt();
            Integer cusType = JSON.parseObject(soExt).getInteger(WumartBasicContext.CUSTYPE);
            OfcCustomer customer = this.ofcCustomerService.getCustomer(so.getAddressCode(), cusType, so.getRegionCode());
            customer.setSupplyOrg(so.getSupplierOrg());
            customer.setDc(so.getSupplierDc());
            customer.setSupplierId(so.getSupplierId());
            customer.setSupplierCode(so.getSupplierCode());
            customer.setSupplierGroup(so.getSupplierGroup());
            Future<Boolean> future = this.executor.submit(CreateSoHandler.newInstance(this.context, ofcOrderHead, so, customer));
            futures.add(future);
        }

        for (Future<Boolean> future : futures) {
            try {
                if (!future.get()) {
                    logger.error("创建SO失败，订单号=" + orderCode);
                    return -1;
                }
            } catch (Exception e) {
                String msg = "创建SO异常，订单号=" + orderCode + "\n" + e.getMessage();
                logger.error(msg, e);
                throw new BusinessException(EC.ERROR.getCode(), msg, e);
            }
        }
        return sos.size();
    }

    @Override
    public int merger(Long orderCode) throws BusinessException {
        OfcSoHead filter = new OfcSoHead();
        filter.setOrderCode(orderCode);
        List<OfcSoHead> sos = this.ofcSoService.findList(filter, true);
        if (CollectionUtils.isEmpty(sos)) {
            throw EC.SO_OBD_NOT_EXIST.exception("订单号=" + orderCode);
        }

        OfcOrderHead param = new OfcOrderHead();
        param.setOrderCode(orderCode);
        OfcOrderHead ofcOrderHead = this.ofcOrderService.findList(param, true).get(0);
        if (ofcOrderHead == null) {
            throw EC.ORDER_IS_NULL.exception("订单号=" + orderCode);
        }

        // 解析logisticsMode，物流模式
        Integer logisticsType = null;
        if (!StringUtils.isEmpty(ofcOrderHead.getExt())) {
            JSONObject ext = JSON.parseObject(ofcOrderHead.getExt());
            logisticsType = ext.getInteger(Constants.OFC_ORDER_LOGISTICS_MODE);
        }

        logger.info("SO Merge，订单号= " + orderCode + "; 物流模式：" + logisticsType);
        if (logisticsType == null || logisticsType.intValue() == 0) {
            throw EC.ORDER_LOGISTICS_IS_EMPTY.exception("订单号=" + orderCode);
        }

        Future<Boolean> future = executor.submit(AbstractLogisticsHandler.newInstance(context, logisticsType, ofcOrderHead, sos));

        try {
            if (!future.get()) {
                logger.error("SO Merge失败，订单号=" + orderCode);
                return -1;
            }
        } catch (Exception e) {
            String msg = "SO Merge失败异常，订单号=" + orderCode + "\n" + e.getMessage();
            logger.error(msg, e);
            throw new BusinessException(EC.ERROR.getCode(), msg, e);
        }


        return sos.size();
    }

//    /**
//     * 统计箱数
//     *
//     * @param details 详情
//     * @return 返回值
//     * @throws BusinessException 异常
//     */
//    private BigDecimal sumBoxQty(List<OfcOrderDetail> details) throws BusinessException {
//        if (CollectionUtils.isEmpty(details)) {
//            return BigDecimal.ZERO;
//        }
//        BigDecimal eaQty = BigDecimal.ZERO;
//        BigDecimal boxQty = BigDecimal.ZERO;
//        for (OfcOrderDetail detail : details) {
//            if (detail == null) {
//                continue;
//            }
//            Long skuCode = detail.getSkuCode();
//            Integer define = JSON.parseObject(detail.getExt()).getInteger(Constants.ORDER_D_SKU_DEFINE);
//            if (SkuDefine.EA.getCode().equals(define)) {
//                if (detail.getGoodsSaleUnit().compareTo(BigDecimal.ONE) == 0) {
//                    eaQty = eaQty.add(detail.getGoodsQty());
//                } else {
//                    boxQty = boxQty.add(detail.getGoodsQty());
//                }
//            } else if (SkuDefine.BOX.getCode().equals(define)) {
//                logger.warn("sku_define=2, sku_code=" + skuCode);
//                boxQty = boxQty.add(detail.getGoodsQty());
//            } else if (SkuDefine.KG.getCode().equals(define)) {
//                logger.warn("sku_define=3, sku_code=" + skuCode);
//                if (detail.getGoodsSaleUnit().compareTo(BigDecimal.ONE) == 0) {
//                    eaQty = eaQty.add(detail.getGoodsQty());
//                } else {
//                    boxQty = boxQty.add(detail.getGoodsQty());
//                }
//            } else {
//                throw EC.ERROR.exception("sku_define is error! define=" + define + ", sku_code=" + skuCode);
//            }
//        }
//
//        logger.info("Order[" + details.get(0).getOrderCode() + "] details unit: ea=" + eaQty + ", box=" + boxQty);
//        return boxQty.add(eaQty.divide(BigDecimal.valueOf(20), 2, BigDecimal.ROUND_HALF_UP));
//    }

    @Override
    public CommonResult<Boolean> callback(JSONObject data) throws BusinessException {
        String soBillCode = data.getString("reqCode");
        if (!StringUtils.hasText(soBillCode)) {
            throw EC.ERROR_PARAMS.exception("reqCode[" + soBillCode + "] is black! received content:" + data.toJSONString());
        }

        // TODO 2019-04-02 汇总so
        if (soBillCode.startsWith("lk") || soBillCode.startsWith("ll")) {
            logger.info("汇总订单so回调信息是 " + data.toJSONString());
            this.saveTotalSo2Redis(soBillCode, data.toJSONString());
            return CommonResult.success(true);
        }

        OfcSoHead filter = new OfcSoHead();
        filter.setSoBillCode(soBillCode);
        List<OfcSoHead> sos = this.ofcSoService.findList(filter, true);
        if (CollectionUtils.isEmpty(sos)) {
            throw EC.SO_ORDER_NOT_EXIST.exception("reqCode[" + soBillCode + "] is not match! received content:" + data.toJSONString());
        }

        OfcSoHead so = sos.get(0);
        Integer soStatus = so.getSoStatus();
        MethodCallLogCollector.business(so.getSoBillCode(), 10);

        if (SoStatus.CREATED.getValue().equals(soStatus)) {
            String msg = "SO[" + soBillCode + "]create so callback duplicate! received content:" + data.toJSONString();
            return CommonResult.success(true, msg);
        }

        if (!SoStatus.CREATING.getValue().equals(soStatus) && !SoStatus.CREATE_FAIL.getValue().equals(soStatus)) {
            throw EC.ERROR.exception("SO[" + soBillCode + "] status is incorrect! status:" + soStatus + ", received content:" + data.toJSONString());
        }

        String soCode = data.getString("soCode");
        JSONArray items = data.getJSONArray("details");
        if (!StringUtils.hasText(soCode) || "-1".equals(soCode) || CollectionUtils.isEmpty(items)) {
            OfcSoHead updateHead = new OfcSoHead();
            updateHead.setId(so.getId());
            updateHead.setSoBillCode(so.getSoBillCode());
            updateHead.setSoStatus(SoStatus.CREATE_FAIL.getValue());
            int ret = this.ofcSoService.update4Create(updateHead, SoStatus.valueOf(soStatus), SoStatus.CREATE_FAIL, so.getVenderId());
            String msg = "SO[" + soBillCode + "]create so error! ret=" + ret + "... received content:" + data.toJSONString();
            logger.warn(msg);
            return CommonResult.error(msg, false);
        }

        Map<Integer, OfcSoDetail> detailMap = new HashMap<>();
        for (OfcSoDetail detail : so.getDetails()) {
            detailMap.put(detail.getItemNo(), detail);
        }
        BigDecimal totalSkuQty = BigDecimal.ZERO;
        List<OfcSoDetail> updateDetails = new ArrayList<>(detailMap.size());
        for (int i = 0; i < items.size(); i++) {
            JSONObject item = items.getJSONObject(i);
            //行项目号
            Integer itemNbr = item.getInteger("no");
            //物美码
            String wmcode = item.getString("code");
            //请求SKU数量
            BigDecimal reqQty = item.getBigDecimal("reqQty");
            //创建SKU数量
            BigDecimal retQty = item.getBigDecimal("retQty");

            OfcSoDetail soDetail = detailMap.get(itemNbr);
            if (soDetail == null) {
                throw EC.ERROR.exception("SO[" + soBillCode + "]Item is not exist! no=" + item.getString("no") + "... received content:" + data.toJSONString());
            }
            if (retQty == null || retQty.compareTo(BigDecimal.ZERO) < 0) {
                retQty = BigDecimal.ZERO;
            }
            //物美码不一致，或者请求数量不一致
            if (!soDetail.getSkuSupplyCode().equals(wmcode) || soDetail.getSkuOrderQty().compareTo(reqQty) != 0) {
                throw EC.ERROR.exception("SO[" + soBillCode + "]Item is not match! code(" + wmcode + "," + soDetail.getSkuSupplyCode() + "), reqQty(" + reqQty + "," + soDetail.getSkuOrderQty() + ") no=" + item.getString("no") + "... received content:" + data.toJSONString());
            }
            JSONObject obj = JSON.parseObject(soDetail.getExt());
            obj.put(Constants.SO_D_OBD, item.getString("obd"));

            String callbackSupplyPrice = item.getString("price");
            if (StringUtils.hasText(callbackSupplyPrice)) {
                obj.put(Constants.OFC_SO_WUMART_CALLBACK_SUPPLY_PRICE, callbackSupplyPrice.trim());
            }

            OfcSoDetail updateDetail = new OfcSoDetail();
            updateDetail.setId(soDetail.getId());
            updateDetail.setSkuSupplyQty(retQty);
            updateDetail.setExt(obj.toJSONString());
            updateDetails.add(updateDetail);
            totalSkuQty = totalSkuQty.add(retQty);
        }

        JSONObject ext = JSON.parseObject(so.getExt());
        ext.put(Constants.SO_H_REF_SO_CODE, data.get("temp3"));
        ext.put(Constants.SO_H_FULFILL_CREATE_TIME, OFCUtils.currentTime());
        OfcSoHead updateHead = new OfcSoHead();
        updateHead.setId(so.getId());
        updateHead.setSoBillCode(so.getSoBillCode());
        updateHead.setSoStatus(SoStatus.CREATED.getValue());
        updateHead.setSoCode(soCode);
        updateHead.setTotalSkuSupplyQty(totalSkuQty);
        updateHead.setExt(ext.toJSONString());
        updateHead.setDetails(updateDetails);
        int ret = this.ofcSoService.update4Create(updateHead, SoStatus.valueOf(soStatus), SoStatus.CREATED, so.getVenderId());
        logger.info("SO[" + soBillCode + "]create so callback success! ret=" + ret + "... received content:" + data.toJSONString());

        if (FulfillChannel.isWumartOfc(FulfillChannel.valueOf(so.getFulfillChannel())) && totalSkuQty.doubleValue() > 0) {
            asyncTaskHandler.buildAsyncTask(so, OfcTaskType.SO_VALIDATE);
        }

        return CommonResult.success(true);
    }

    /**
     * @param soBillCode
     * @param content
     */
    private void saveTotalSo2Redis(String soBillCode, String content) {
        String wumartOFCTotalSoKey = MessageFormat.format(Constants.WUMART_OFC_TOTAL_SO, soBillCode);
        redisTemplate.set(wumartOFCTotalSoKey, content);
    }


}
